/*
 * Copyright (C) 2011 Urals State University,
 * Department of Mathematics and Mechanics
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ru.edu.usu.cs.bubbles;

import java.util.Vector;

import ru.edu.usu.cs.bubbles.engine.GameEntity;
import ru.edu.usu.cs.bubbles.engine.GameWorld;
import ru.edu.usu.cs.bubbles.engine.box2d.Box2dGameWorld;
import ru.edu.usu.cs.bubbles.userlistener.Trace;
import ru.edu.usu.cs.bubbles.userlistener.UserListener;
import ru.edu.usu.cs.bubbles.utils.Graphics;
import ru.edu.usu.cs.bubbles.utils.Point;
import ru.edu.usu.cs.bubbles.utils.TtlOut;
import android.content.Context;
import android.graphics.Canvas;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

/**
 * Поток в котором происходит отрисовка (через SurfaceHolder) и симуляция
 * внутреннего состояния игры.
 */
public class GameThread extends Thread {
	public final static int FRAMES_PER_SECOND = 25;
	public final static int GAME_TICK = 1000 / FRAMES_PER_SECOND;
	private final static String TAG = "GameThread";
	private final static float MIN_DIST = 1f;

	/**
	 * Возможные состояния игры.
	 */
	public enum GameState {
		READY, RUNNING, PAUSE, WIN, LOSE
	}

	private boolean isRunning;
	private GameState gameState;
	private SurfaceHolder surfaceHolder;
	private Context context;
	private GameWorld world;
	private int level;

	private UserListener on_touch;

	/*
	 * used in setSurfaceSize. Maybe will be usefull private int SurfaceHeight =
	 * 1; private int SurfaceWidth = 1;
	 */
	public GameThread(SurfaceHolder surfaceHolder, Context context, int level,
			UserListener on_touch) {
		this.surfaceHolder = surfaceHolder;
		this.context = context;
		this.level = level;
		this.on_touch = on_touch;
		world = new Box2dGameWorld(new Point(0, 10));

		// load information
		new Graphics(this.context).loadImage();// initialized static fields in
		// Graphics
		loadLevel();
		gameState = GameState.READY;
	}

	private void loadLevel() {
		switch (level) {
		case 0:
			Log.d(TAG, "load level 0");
			world.createBubble(100, 100, 20);
			world.createBubble(120, 120, 20);
			// создание пола, жестко привязанного к координатам: временно
			world.createIceWall(50, 10, new Point(35, 400),
					(float) (Math.PI / 3));
			world.createWall(50, 10, new Point(75, 425), 0);
			world.createWall(50, 10, new Point(125, 425), 0);
			world.createWall(50, 10, new Point(175, 425), 0);
			world.createWall(50, 10, new Point(225, 425), 0);
			world.createWall(50, 10, new Point(265, 400),
					(float) ((Math.PI / 4) * 3));
			world.createWall(50, 10, new Point(300, 350),
					(float) ((Math.PI / 4) * 3));
			world.createWhirlpool(150, 200, 30, 5);
			break;

		default:
			break;
		}
	}

	@Override
	/**
	 * основной цикл игры
	 */
	public void run() {
		gameState = GameState.RUNNING;
		Log.d(TAG, "running isrunning - " + isRunning);
		Log.d(TAG, "Gamestate:" + gameState);
		while (isRunning) {
			long begin = System.currentTimeMillis();

			if (on_touch.EventCreated) {
				// TODO Van refactor this!
				MotionEvent event = on_touch.getEventToProcessing();
				Vector<Point> points = new Vector<Point>();
				int history_size = event.getHistorySize();
				float x, y;
				for (int i = 0; i < history_size; i++) {
					Point point_a = points.lastElement();
					x = event.getHistoricalX(i);
					y = event.getHistoricalY(i);
					Point point_b = new Point(x > 0 ? x : 0, y > 0 ? y : 0);
					if (Math.sqrt((point_b.x - point_a.x)
							* (point_b.x - point_a.x) + (point_b.y - point_a.y)
							* (point_b.y - point_a.y)) >= MIN_DIST)
						points.add(point_b);
				}
				x = event.getX();
				y = event.getY();
				points.add(new Point(x > 0 ? x : 0, y > 0 ? y : 0));
				Trace trace = Trace.fromVector(points);
				x = trace.getCenter(0).x;
				y = trace.getCenter(0).y;
				// добавить объекты от юзера
				world.createIceWall(50, 10, new Point(x,y), 0);
			}

			if (gameState == GameState.RUNNING) {
				updatePhysics();
				updateControllers();
			}

			redraw();

			long elapsed = (int) (System.currentTimeMillis() - begin);
			long sleepTime = GAME_TICK - elapsed;

			if (sleepTime > 0) {
				try {
					Thread.sleep(sleepTime);
				} catch (InterruptedException e) {
				}
			} else {
				Log.w(TAG, "We are running behind! sleepTime: " + sleepTime);
			}
		}
		Log.e(TAG, "stop running");
	}

	/**
	 * Отрисовывает графику в зависимости от состояния игры
	 */
	private void redraw() {

		Canvas canvas = null;
		if (gameState == GameState.RUNNING) {
			try {
				canvas = surfaceHolder.lockCanvas();
				synchronized (surfaceHolder) {
					canvas.drawBitmap(Graphics.BackgroundImage, 0, 0, null);
					for (GameEntity entity : world.getEntities())
						if (entity.representation != null)
							entity.representation.draw(canvas);
						else
							Log.w(TAG, "Object without representation: "
									+ entity.getClass());
				}

			} finally {
				if (canvas != null)
					surfaceHolder.unlockCanvasAndPost(canvas);
			}
		}
	}

	private void updateControllers() {
		for (GameEntity entity : world.getEntities())
			// у обьекта может отсутствовать контроллер, это нормально
			try {
				if (entity.controller != null) {

					entity.controller.processStep();
				}
			} catch (TtlOut e) {
				// TODO возможно, не удаляются ссылки на GameEntity - взаимные
				// круговые ссылки на контроллеры и representation
				world.markDestroy(entity);
			}
		// маркировка и удаление сделаны отдельно, так как удалять объект
		// GameEntity из списка всех entities нельзя во время перечисления
		world.destoyAllMarked();
	}

	private void updatePhysics() {
		world.step((float) GAME_TICK / 1000);
	}

	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}

	public void setGameState(GameState state) {
		gameState = state;
	}

	public void setSurfaceSize(int width, int height) {
		synchronized (surfaceHolder) {
			// SurfaceWidth = width;
			// SurfaceHeight = height;

			// don't forget to resize the background image
			Graphics.resizeBackground(width, height);
		}

	}
}
